using System;
using System.Linq.Expressions;
using System.Windows;
using System.Windows.Controls.Primitives;
using FluentSilverlight.Commands;
using FluentSilverlight.Utility.Reflection;

namespace FluentSilverlight.Interceptors
{
    public class CommandVisibilityBinder<TModel, TView>
        where TView : ButtonBase
    {
        private readonly TView element;
        private readonly IFsCommand command;
        private readonly bool hideIfCannotExecuteCommand;
        private Visibility viewVisibility;
        private readonly ButtonBaseVisibilityInterceptor<TModel, TView> interceptor;

        public CommandVisibilityBinder(TView element, IFsCommand command, bool hideIfCannotExecuteCommand,
                                       TModel model, Expression<Func<TModel, object>> visibleExpression)
        {
            this.element = element;
            this.hideIfCannotExecuteCommand = hideIfCannotExecuteCommand;

            this.command = command;
            this.command.CanExecuteChanged += (sender, e) => SetVisibility();

            var modelPropertyName = ReflectionHelper.GetAccessor(visibleExpression).Name;
            interceptor = new ButtonBaseVisibilityInterceptor<TModel, TView>(element, model, modelPropertyName);
            interceptor.ModelIsVisibleChanged += OnModelIsVisibleChanged;
            interceptor.ViewVisiblilityChanged += OnViewVisibilityChanged;
            interceptor.Initialize();

            // initialization
            SetVisibility();
        }

        private void OnViewVisibilityChanged(object sender, EventArgs<Visibility> e)
        {
            viewVisibility = e.Value;
            interceptor.SetModelIsVisible(ViewIsVisible);
        }

        private void OnModelIsVisibleChanged(object sender, EventArgs<bool> e)
        {
            viewVisibility = e.Value.ToVisibility();
            interceptor.SetViewVisibility(viewVisibility);
            SetVisibility();
        }

        private void SetVisibility()
        {
            element.Visibility = IsVisible.ToVisibility();
        }

        public bool ViewIsVisible { get { return viewVisibility.ToBoolean(); } }

        public bool CommandIsVisible { get { return command.CanExecute() || !hideIfCannotExecuteCommand; } }

        public bool IsVisible { get { return ViewIsVisible && CommandIsVisible; } }
    }
}